package main

import (
	"encoding/json"
	"errors" 
	"fmt"
	"github.com/hyperledger/fabric-contract-api-go/contractapi"
	"strconv"
	"sort"
	"reflect"
)

type User struct {
	ID      string  `json:"id"`
	Token   float64 `json:"token"`
	Inputs   float64   `json:"Inputs"`
	Outputs  float64   `json:"Outputs"`
	Orders 	[]string 	`json:"orders"`
	Total   float64     `json:"total"`
}

type Auction struct {
	AuctionId string `json:"auctionId"`
	BuyerId  []string	`json:"buyerId"`
	SellerId []string `json:"sellerId"`
	Price  float64 `json:"price"`
	Amount float64 `json:"amount"`
	Status int `json:"status"`	//订单状态 0 未完成 1完成
	Time string `json:"time"`
}

type TradeInfo struct{
	UserId   string   `json:"userId"`	//用户id
	Price   float64   `json:"price"`	//请求单价
	Amount   float64   `json:"amount"`	//请求交易数量
	Role 	 int 	   `json:"role"`	//交易角色，0卖家 1买家
	ActualPrice float64   `json:"actualPrice"`	//实际价格
	ActualAmount float64   `json:"actualAmount"`	//实际成交数量
}

type Meter struct{
	UserId   string   `json:"userId"`	//用户id
	Inputs   float64   `json:"Inputs"`	//新增电量
	Outputs  float64   `json:"Outputs"`	//消耗电量
	Amount   float64   `json:"amount"`	//需要消费的电量
	Price float64       `json:"price"`  //单价
}

type MatchResponse struct{
	Buyers    []string
    Sellers   []string
    AuctionID string
	Price 		float64
	Amount	float64
	NewMeters []Meter
}

type Contract struct {
	contractapi.Contract
}

func (c *Contract) InitLedger(ctx contractapi.TransactionContextInterface) error {
	users := [] User{
		{ID:"user0",   Token:100.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user1",   Token:120.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user2",   Token:115.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user3",   Token:117.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user4",   Token:148.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user5",   Token:135.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user6",   Token:140.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user7",   Token:115.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user8",   Token:127.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user9",   Token:128.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user10",  Token:109.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user11",  Token:120.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user12",  Token:111.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user13",  Token:117.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user14",  Token:142.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user15",  Token:135.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user16",  Token:102.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user17",  Token:145.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user18",  Token:122.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user19",  Token:118.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user20",  Token:150.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user21",  Token:132.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user22",  Token:116.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user23",  Token:107.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user24",  Token:148.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user25",  Token:115.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user26",  Token:144.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user27",  Token:127.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user28",  Token:106.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
		{ID:"user29",  Token:133.0, Inputs:0.0, Outputs:0.0, Orders:[]string{} ,Total: 100.0},
	}

	for _, user := range users {
		userJSON, err := json.Marshal(user) 
		if err != nil {
			return err
		}
	
		err = ctx.GetStub().PutState(user.ID, userJSON)
		if err != nil {
			return fmt.Errorf("failed to put to world state. %v", err)
		}
	}

	return nil
}

//	注册用户	("user1","200","100","100","100")
func (c *Contract) RegisterUser(ctx contractapi.TransactionContextInterface,id string, token float64,inputs float64,outputs float64,total float64) (string, error) {
	fmt.Printf("注册用户到账本: %v",id)
	exists, err := c.IsUserExists(ctx, id)
	if err != nil {
		return "",fmt.Errorf("无法检查用户是否存在: %w", err)
	}
	if (exists) {
		return "",errors.New("用户已存在")
	}

	user := &User{
		ID:      id,
		Token:   token,
		Inputs:  inputs,
		Outputs: outputs,
		Orders: []string{},
		Total:   total,
	}
	// 序列化 User 对象
	userJSON, err := json.Marshal(user)
	if err != nil {
		return "",fmt.Errorf("无法序列化用户数据: %w", err)
	}
	// 将 User 对象存储到帐本
	err = ctx.GetStub().PutState(id, userJSON)
	if err != nil {
		return "",fmt.Errorf("无法保存用户数据到帐本: %w", err)
	}
	fmt.Errorf("用户对象 %v 已成功存储到帐本",id)
	
	return string(userJSON),nil
}

// 转账 、 能源余额变化
func (c *Contract) TransferMoney(ctx contractapi.TransactionContextInterface, id string, price float64, amount float64) (string,error) {
	fmt.Printf("执行扣钱操作，扣除金额：%.2f",amount * price)
	userJSON, err := ctx.GetStub().GetState(id)
	if err != nil {
		return "无法获取用户数据",fmt.Errorf("无法获取用户数据: %w", err)
	}
	if userJSON == nil {
		return"用户不存在", errors.New("用户不存在")
	}

	user := &User{}
	err = json.Unmarshal(userJSON, user)
	if err != nil {
		return "无法反序列化用户数据",fmt.Errorf("无法反序列化用户数据: %w", err)
	}

	if user.Token < amount * price {
		return "余额不足",errors.New("余额不足")
	}

	user.Token += amount * price	//修改账户余额
	user.Total -= amount			//修改能源余额
	userJSON, err = json.Marshal(user)
	if err != nil {
		return "无法获取用户数据",fmt.Errorf("无法序列化用户数据: %w", err)
	}

	err = ctx.GetStub().PutState(id, userJSON)
	if err != nil {
		return "无法保存用户数据到帐本",fmt.Errorf("无法保存用户数据到帐本: %w", err)
	}
	fmt.Printf("扣除后余额：%.2f",user.Token)
	return "转账完成",nil
}

// 查询用户信息
func (c *Contract) QueryInfo(ctx contractapi.TransactionContextInterface, id string) (string, error) {
	userJSON, err := ctx.GetStub().GetState(id)
	if err != nil {
		return "无法获取用户 "+id+" 的数据", fmt.Errorf("无法获取用户数据: %w", err)
	}
	if userJSON == nil {
		return "用户 "+id+" 的数据为空", errors.New(string(userJSON))
	}

	return string(userJSON), nil
}

// 判断用户是否存在
func (c *Contract) IsUserExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
	userJSON, err := ctx.GetStub().GetState(id)
	if err != nil {
		return false, fmt.Errorf("无法获取用户数据: %w", err)
	}

	return userJSON != nil, nil
}

//	查询订单
//		params: 订单号
func (c *Contract) queryAuctionById(ctx contractapi.TransactionContextInterface, id string) (*Auction, error) {
	orderJSON, err := ctx.GetStub().GetState(id)
	if err != nil {
		return nil, fmt.Errorf("failed to read from world state: %w", err)
	}

	if orderJSON == nil {
		return nil, fmt.Errorf("order with ID %s does not exist", id)
	}

	order := new(Auction)
	err = json.Unmarshal(orderJSON, order)
	if err != nil {
		return nil, fmt.Errorf("failed to unmarshal order JSON: %w", err)
	}

	return order, nil
}
type ByAmount []Meter
func (a ByAmount) Len() int {return len(a)}
func (a ByAmount) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByAmount) Less(i, j int) bool { return a[i].Amount < a[j].Amount }

// 	MatchTrade：匹配产生交易
func (c *Contract) MatchTrade(ctx contractapi.TransactionContextInterface,userMetersJSON string,random string) (string,error){	 
	response := MatchResponse{
        Buyers:    []string{},
        Sellers:   []string{},
        AuctionID: "",
		Price: 0,
		Amount: 0,
		NewMeters: []Meter{},
    }
	var buyerMeter []Meter
	var sellerMeter []Meter
	// 转对象数组
	var userMeters []Meter
	errConv := json.Unmarshal([]byte(userMetersJSON), &userMeters)
	if errConv != nil {
		return "",fmt.Errorf("Error parsing JSON:", errConv)
	}

	// 归类
	for _,meter := range userMeters{
		if meter.Inputs < meter.Outputs{
			// meter.Amount = meter.Outputs - meter.Inputs
			buyerMeter = append(buyerMeter,meter)
		}else if meter.Inputs > meter.Outputs{
			// meter.Amount = meter.Inputs - meter.Outputs
			sellerMeter = append(sellerMeter,meter)
		}
	}
	sort.Sort(ByAmount(sellerMeter))//卖家根据可卖电量由低到高
	I := len(buyerMeter)
	J := len(sellerMeter)
	for i,j := 0,0;i<I && j<J; {
		if (buyerMeter[i].Amount<=sellerMeter[j].Amount){
			b := buyerMeter[i]
			s := sellerMeter[j]
			// 随机生成auctionId
			auctionId := "A" + random + strconv.Itoa(i)
			// 价格取均值
			price := (b.Price + s.Price)/2.0
			// 记录个体的交易具体信息
			err1 := c.AddTradeInfo(ctx,auctionId,b.UserId,1,b.Price,price,b.Amount,b.Amount)
			err2 := c.AddTradeInfo(ctx,auctionId,s.UserId,0,s.Price,price,s.Amount,b.Amount)
			if( err1!=nil ){
				return "",fmt.Errorf("MatchTrade: failed to put TradeInfo in public data: %v", err1)
			}
			if( err2!=nil ){
				return "",fmt.Errorf("MatchTrade: failed to put TradeInfo in public data: %v", err2)
			}
			// 单号加入用户信息
			err3 := c.AddOders2User(ctx,b.UserId,auctionId)
			err4 := c.AddOders2User(ctx,s.UserId,auctionId)
			if(err3 != nil){
				return "",fmt.Errorf("MatchTrade: failed to AddOders2User in public data: %v", err3)
			}
			if(err4 != nil){
				return "",fmt.Errorf("MatchTrade: failed to AddOders2User in public data: %v", err4)

			}
			// 构建返回结构体
			response.Buyers = []string{b.UserId}
			response.Sellers = []string{s.UserId}
			response.AuctionID = auctionId
			response.Price = price
			response.Amount = b.Amount
			//返回电表数组
			for _,m := range(userMeters){
				if(!reflect.DeepEqual(m, b) && !reflect.DeepEqual(m, s)){
					response.NewMeters = append(response.NewMeters,m)
				}
			}
			// 将结构体实例转换为字节数组
			result, err := json.Marshal(response)
			if err != nil {
				return "", fmt.Errorf("failed to marshal struct: %v"+response.AuctionID, err)
			}
			return string(result),nil
		}else{
			j++
		}
	}
	return "",nil
}

// 将订单信息加入用户信息
func (c *Contract) AddOders2User(ctx contractapi.TransactionContextInterface,id string,auctionID string) error{
	//获取帐本中对象信息
	userJSON, err := ctx.GetStub().GetState(id)
	if err != nil {
		return err
	}
	if userJSON == nil {
		return fmt.Errorf("the user %s does not exist", id)
	}

	// json转对象
	var user User
	err = json.Unmarshal(userJSON, &user)
	if err != nil {
		return err
	}

	// 更新对象信息
	user.Orders = append(user.Orders,auctionID)
	newUserJSON, err := json.Marshal(user)
	if err != nil {
		return err
	}

	// 更新帐本
	err = ctx.GetStub().PutState(id, newUserJSON)
	if err != nil {
		return fmt.Errorf("failed to put user in public data: %v", err)
	}

	return nil
}

// auctionId ,user ,role ,price ,actualPrice ,amount ,actualAmount 
func (c *Contract) AddTradeInfo(ctx contractapi.TransactionContextInterface,auctionId string,user string,role int,price float64,actualPrice float64,amount float64,actualAmount float64)error{
	tradeInfo := &TradeInfo{
		UserId : user,
		Price  : price,
		Amount : amount,
		Role : role,
		ActualPrice : actualPrice,
		ActualAmount : actualAmount,
	}
	tradeInfoJSON, err := json.Marshal(tradeInfo)
	if err != nil {
		return err
	}
	err = ctx.GetStub().PutState(auctionId+ "_" +user, tradeInfoJSON)
	if err != nil {
		return fmt.Errorf("failed to put TradeInfo in public data: %v", err)
	}

	// 设置事件
    err = ctx.GetStub().SetEvent("TradeInfoAdded", tradeInfoJSON)
    if err != nil {
        return fmt.Errorf("failed to set event: %v", err)
    }
	return nil
}

// 创建订单
// 	params:	用户id
func (c *Contract) CreateAuction(ctx contractapi.TransactionContextInterface,buyers []string,sellers []string,auctionId string,time string) (string , error) {
	tradePrice := 0.0;//交易价格
	tradeAmount := 0.0;//交易数量
	// 调用TransferMoney扣钱 
	// 买家
	for _,buyerId := range buyers{
		id := auctionId + "_" +buyerId
		// 获取该用户的具体交易信息对象
		tradeInfoJSON, err := ctx.GetStub().GetState(id)
		if err != nil {
			return "无法获取tradeInfoJSON数据",fmt.Errorf("无法获取tradeInfoJSON数据: %w", err)
		}
		if tradeInfoJSON == nil {
			return "tradeInfo不存在", errors.New("tradeInfoJSON不存在,id: " +id)
		}
		// 转对象
		tradeInfoObj := &TradeInfo{}
		err = json.Unmarshal(tradeInfoJSON, tradeInfoObj)
		if err != nil {
			return "无法反序列化用户数据",fmt.Errorf("无法反序列化用户数据: %w", err)
		}
		// 转账
		msg , err1 := c.TransferMoney(ctx,buyerId,tradeInfoObj.ActualPrice ,-tradeInfoObj.ActualAmount )
		if err1 != nil {
			return "转账错误，用户："+msg,fmt.Errorf("扣钱错误: %w", err1)
		}
		tradePrice = tradeInfoObj.ActualPrice
		tradeAmount = tradeInfoObj.ActualAmount
	}
	// 卖家
	for _,sellerId := range sellers{
		// 获取该用户的具体交易信息对象
		tradeInfoJSON, err := ctx.GetStub().GetState(auctionId+ "_"+sellerId)
		if err != nil {
			return "无法获取tradeInfoJSON数据",fmt.Errorf("无法获取tradeInfoJSON数据: %w"+auctionId+ "_"+sellerId, err)
		}
		if tradeInfoJSON == nil {
			return "tradeInfo不存在", errors.New("tradeInfoJSON不存在")
		}
		// 转对象
		tradeInfoObj := &TradeInfo{}
		err = json.Unmarshal(tradeInfoJSON, tradeInfoObj)
		if err != nil {
			return "无法反序列化用户数据",fmt.Errorf("无法反序列化用户数据: %w", err)
		}
		// 扣钱
		msg, err1 := c.TransferMoney(ctx,sellerId,tradeInfoObj.ActualPrice ,tradeInfoObj.ActualAmount )
		if err != nil {
			return "扣钱错误，用户："+msg,fmt.Errorf("转账错误: %w", err1)
		}
	}
	fmt.Println("TransferMoney success")
	auction := &Auction{
		AuctionId : auctionId,
		BuyerId  : buyers,
		SellerId : sellers,
		Price  : tradePrice,
		Amount : tradeAmount,
		Status: 0,
		Time: time,
	}
	auctionJSON, err := json.Marshal(auction)
	if err != nil {
		return "创建订单出错",err
	}
	// put auction into state
	err = ctx.GetStub().PutState(auctionId, auctionJSON)
	if err != nil {
		return "创建订单出错",fmt.Errorf("failed to put auction in public data: %v", err)
	}

	return auctionId,nil
}

func (c *Contract) AddInfo(ctx contractapi.TransactionContextInterface,buyers []string,sellers []string,auctionId string,time string) (string , error) {
	
}

//充放电
// 参数：id string,amount float64
func (c *Contract) Charge(ctx contractapi.TransactionContextInterface, id string,amount float64)(string, error){
	// 获取用户信息
	// 修改电表信息
	// 调用TESP充放电
	return "",nil
}

func main() {
	chaincode, err := contractapi.NewChaincode(&Contract{})
	if err != nil {
		fmt.Printf("无法创建合约: %s", err.Error())
		return
	}

	if err := chaincode.Start(); err != nil {
		fmt.Printf("无法启动合约: %s", err.Error())
	}
}
